﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Linq.Expressions;
using System.Web.Mvc;
using System.Web.Routing;
using System.Reflection;

namespace ASPMVCUtil.Helpers.Html.Grid
{
    /// <summary>
    /// Clase para configurar el grid. Contiene las configuraciones de una columna del mismo
    /// </summary>
    /// <typeparam name="TModel"></typeparam>
    public class GridColumn<TModel>
    {
        public string Width { get; protected set; }
        public string PropertyName { get; protected set; }
        public string Header { get; protected set; }
        public bool Ordenable { get; protected set; }
        public bool Visible { get; protected set; }
        public string[] AdditionalCSSClasses { get; protected set; }
        public virtual GridCellTemplate<TModel> Template { get; protected set; }

        /// <summary>
        /// Crea una columna de grid a partir de almenos su nombre  
        /// </summary>
        /// <param name="propertyName">Nombre de la propiedad, debe ser exacto al nombre de la propiedad en TModel y de la forma Propiedad[.SubPropiedad]. Será usado como título de la columna también</param>
        /// <param name="ancho">Debe ser * (para que tome todo el espacio que pueda) o un número</param>
        /// <param name="ordenable">Indica si esta columna debe tener habilitado el ordenamiento</param>
        /// <returns></returns>
        public static GridColumn<TModel> create(string propertyName, string ancho = "*", bool ordenable = true, GridCellTemplate<TModel> template = null, string[] additionalCSSClasses = null)
        {
            return create(propertyName, ancho, propertyName, true, ordenable, template, additionalCSSClasses);
        }

        /// <summary>
        /// Crea una columna de grid a partir de almenos su nombre, ancho y el titulo
        /// </summary>
        /// <param name="propertyName">Nombre de la propiedad, debe ser exacto al nombre de la propiedad en TModel y de la forma Propiedad[.SubPropiedad]</param>
        /// <param name="width">Debe ser * (para que tome todo el espacio que pueda) o un número</param>
        /// <param name="header">Es el título visible de la columna</param>
        /// <param name="visible">Opcional. Indica si esta columna debe mostrarse en el grid</param>
        /// <param name="ordenable">Opcional. Indica si esta columna debe tener habilitado el ordenamiento</param>
        /// <param name="template">Opcional. Plantilla usada para hacer render de cada celda de la columna</param>
        /// <returns></returns>
        public static GridColumn<TModel> create(string propertyName, string width, string header, bool visible = true, bool ordenable = true, GridCellTemplate<TModel> template = null, string[] additionalCSSClasses = null)
        {
            if (!width.Equals("*"))
            {
                try
                {
                    Convert.ToInt32(width);
                }
                catch (Exception)
                {
                    throw new ArgumentException("The width of the column must be an integer or *");
                }
            }
            var column = new GridColumn<TModel>();
            column.Width = width;
            column.PropertyName = propertyName;
            column.Header = header;
            column.Ordenable = ordenable;
            column.Visible = visible;
            if (template == null)
                column.Template = new GridCellTemplate<TModel>();
            else
                column.Template = template;
            column.AdditionalCSSClasses = additionalCSSClasses;
            return column;
        }

        /// <summary>
        /// Obtiene el valor formateado de una celda de la columna a partir de un registro. Además adjunta a la ruta para acciones AJAX el valor limpio de la celda
        /// </summary>
        /// <param name="record"></param>
        /// <param name="routeValues"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public string getValue(TModel record, RouteValueDictionary routeValues, Key key)
        {
            //Obtengo la propiedad de la columna para el registro
            string[] splitProperty = PropertyName.Split('.');
            PropertyInfo propiedad = null;
            object valorCampo = null;
            foreach (string propertyName in splitProperty)
            {
                if (propertyName.Equals(splitProperty[0]))
                    propiedad = record.GetType().GetProperty(propertyName);
                else if (valorCampo == null)
                    break;
                else
                    propiedad = valorCampo.GetType().GetProperty(propertyName);
                if (propiedad == null)
                    throw new ArgumentException("La propiedad " + PropertyName + " de " + propiedad.Name + " indicada en la columna no existe en el tipo indicado, revise que no sea un error tipográfico");
                if (propiedad.CanRead)
                {
                    if (propertyName.Equals(splitProperty[0]))
                        valorCampo = propiedad.GetValue(record, null);
                    else
                        valorCampo = propiedad.GetValue(valorCampo, null);
                }
                else
                    throw new ArgumentException("La propiedad " + PropertyName + " de " + propiedad.Name + " no se puede leer");
            }
            addValueToRouteValues(routeValues, PropertyName, valorCampo);
            return Template.Draw(valorCampo, record);
        }

        private void addValueToRouteValues(RouteValueDictionary routeValues, string key, object value)
        {
            if(!routeValues.Keys.Contains(key))
                routeValues.Add(key, value);
        }
    }
}